home *** CD-ROM | disk | FTP | other *** search
Text File | 1994-10-04 | 23.1 KB | 781 lines | [TEXT/MMCC] |
- /*================================================================================
- SendFinderTricks.cp
-
- ©1994 Greg Anderson
- greggor@apple.com
-
- Send events to the Finder
-
- ================================================================================*/
- #include "SendFinderTricks.h"
- #include "AppleEventUtilities.h"
- #include "AERegistry.h"
- #include "AEObjects.h"
- #include "FinderRegistry.h"
- #include "Exceptions.h"
-
- //----------------------------------------------------------------------------------------
- // FindPSNbyTypeAndSig:
- //
- // Find the PSN of some process, given its type and creator
- //----------------------------------------------------------------------------------------
- void FindPSNbyTypeAndSig(ProcessSerialNumber* psn, OSType type, OSType sig)
- {
- ProcessInfoRec theProc;
-
- //
- // Start out with kNoProcess
- //
- psn->highLongOfPSN = 0;
- psn->lowLongOfPSN = kNoProcess;
-
- //
- // Initialize fields in the ProcessInfoRec,
- // or we'll have memory hits in random locations
- //
- theProc.processInfoLength = sizeof( ProcessInfoRec );
- theProc.processName = nil;
- theProc.processAppSpec = nil;
- theProc.processLocation = nil;
-
- while(true)
- {
- OSErr theErr;
-
- //
- // Keep looking for the Finder until we find it
- //
- FailErr(GetNextProcess(psn));
- if( (psn->highLongOfPSN == 0) && (psn->lowLongOfPSN == kNoProcess) )
- Throw(procNotFound);
-
- //
- // Is the current process the one we're looking for?
- //
- FailErr(GetProcessInformation(psn, &theProc));
- if( (theProc.processType == type ) && (theProc.processSignature == sig) )
- break;
- }
- } // FindPSNbyTypeAndSig
-
- TDescriptor GetAddressOfFinder();
-
- #ifdef SOMEWHEREELSE
-
- //----------------------------------------------------------------------------------------
- // GetAddressOfFinder:
- //
- // Generate an address for the Finder on this machine
- //----------------------------------------------------------------------------------------
- TDescriptor GetAddressOfFinder()
- {
- ProcessSerialNumber finderPSN;
- TDescriptor finderAddressDescriptor;
-
- //
- // Find the Finder's PSN
- //
- FindPSNbyTypeAndSig(&finderPSN, 'FNDR', 'MACS');
-
- //
- // Copy the target ID into a descriptor
- //
- finderAddressDescriptor.MakeProcessSerialNumber(finderPSN);
-
- return finderAddressDescriptor;
- } // GetAddressOfFinder
-
- #endif
-
- //----------------------------------------------------------------------------------------
- // MakeSpecifierForSelection
- //----------------------------------------------------------------------------------------
- TDescriptor MakeSpecifierForSelection()
- {
- TDescriptor selectionSpecifier;
- TDescriptor nullDescriptor;
- TDescriptor keyData;
-
- //
- // Make a descriptor whose type is 'typeType' and whose
- // contents are 'pSelection' (defined in FinderRegistry.h).
- // This descriptor specifies the property of the null container
- // that we are interested in--in this case, the selection.
- //
- keyData.MakeDescType(pSelection);
-
- //
- // Make an object specifier for "selection of <null container>".
- // The object specifier will contain the following:
- //
- // Desired class: cProperty Object specifiers that
- // specify properties always
- // have a desired class of cProperty
- //
- // Container: null A null descriptor specifies the
- // null container. Object specifiers
- // almost always terminate with the
- // null descriptor, the only exceptions
- // being specifiers used to support
- // "paste reference," which terminate
- // in the PSN of the process that
- // generated them, and object specifiers
- // contained in test descriptors
- // (found in "whose" clauses) that
- // often terminate with "object being
- // examined".
- //
- // Key form: formPropertyID Object specifiers that specify properties
- // always have a key form of formPropertyID.
- //
- // Key data: typeType / The key data descriptor of a property
- // pSelection must always be of type typeType; in
- // our case, it is a descriptor that
- // contains pSelection (built above).
- //
- // We pass 'true' to MakeObjectSpecifier, so it will always dispose its inputs.
- // If we passed in 'false', we would have to dispose of the keyData descriptor
- // ourselves.
- //
- selectionSpecifier.MakeObjectSpecifier(cProperty, nullDescriptor, formPropertyID, keyData, true);
-
- return selectionSpecifier;
- } // MakeSpecifierForSelection
-
- //----------------------------------------------------------------------------------------
- // MakeSpecifierForIndexedItem
- //----------------------------------------------------------------------------------------
- TDescriptor MakeSpecifierForIndexedItem(DescType desiredClass, long index, TDescriptor ofSpecifier)
- {
- TDescriptor resultSpecifier;
- TDescriptor keyData;
-
- //
- // Make a descriptor whose type is 'typeLongInteger' and whose
- // contents is the index passed in.
- //
- keyData.MakeLong(index);
-
- //
- // Make an object specifier for "<<class desiredClass>> index of <ofSpecifier>".
- // The object specifier will contain the following:
- //
- // Desired class: desiredClass The class of the object being referenced;
- // examples include cWindow, cObject, cDisk,
- // cAliasFile, and many others
- //
- // Container: ofSpecifier Object specifiers are defined recursively
- //
- // Key form: formAbsolutePosition This indicates that we are accessing the
- // 'index'th item of the container. Another
- // choice would be formName, in which case
- // the key data would contain the name of the
- // object being referenced.
- //
- // Key data: typeLongInteger / formAbsolutePosition implies that the key data
- // 'index' will be an integer
- //
- // We pass 'false' to MakeObjectSpecifier, so our inputs are not disposed
- // (we want to keep 'ofSpecifier' around). Because we didn't ask MakeObjectSpecifier
- // to delete our inputs, we'll have to delete the key data ourselves
- //
- resultSpecifier.MakeObjectSpecifier(desiredClass, ofSpecifier, formAbsolutePosition, keyData, false);
- keyData.Dispose();
-
- return resultSpecifier;
- } // MakeSpecifierForSelection
-
- //----------------------------------------------------------------------------------------
- // MakeSpecifierForFrontWindow
- //----------------------------------------------------------------------------------------
- TDescriptor MakeSpecifierForFrontWindow()
- {
- TDescriptor nullDescriptor;
-
- return MakeSpecifierForIndexedItem(cWindow, 1, nullDescriptor);
- }
-
- //----------------------------------------------------------------------------------------
- // MakeSpecifierForPropertyOfSpecifier:
- //
- // This function makes an object specifier for "property of <<specifier provided>>"
- //----------------------------------------------------------------------------------------
- TDescriptor MakeSpecifierForPropertyOfSpecifier(DescType property, TDescriptor ofSpecifier)
- {
- TDescriptor specifier;
- TDescriptor keyData;
- OSErr err = noErr;
-
- //
- // Make a descriptor whose type is 'typeType', and whose
- // contents are the property passed into this function.
- //
- keyData.MakeDescType(property);
-
- Try
- {
- //
- // The next object specifier we make also specifies a property, so it
- // will look very similar to the one we built above:
- //
- // Desired class cProperty Required to specify a property.
- //
- // Container "ofSpecifier" We are making a specifier for
- // "property of <ofSpecifier>", so clearly
- // our container must be the
- // object specifier passed in.
- //
- // Key form: formPropertyID Required to specify a property.
- //
- // Key data typeType / As before, the key data descriptor
- // property is typeType, and contains the property ID
- // that we wish to specify.
- //
- // This time we pass 'false' to MakeObjectSpecifier so that it will NOT dispose
- // of its inputs when it is done. Doing this leaves the keyData and ofSpecifier
- // descriptors intact; we dispose of the key data, but leave the specifier that
- // was passed into this function the responsibility of the caller.
- //
- specifier.MakeObjectSpecifier(cProperty, ofSpecifier, formPropertyID, keyData, false);
- keyData.Dispose();
- }
- Catch(err)
- {
- //
- // Note that 'MakeObjectSpecifier' might fail (out of memory, for example),
- // so we must catch failures to dispose of the key data descriptor if necessary.
- //
- keyData.Dispose();
- Throw(err);
- }
-
- return specifier;
- } // MakeSpecifierForPropertyOfSpecifier
-
- //----------------------------------------------------------------------------------------
- // MakeSpecifierForPropertyOfSelection:
- //
- // This function makes an object specifier for "property of selection"
- //----------------------------------------------------------------------------------------
- TDescriptor MakeSpecifierForPropertyOfSelection(DescType property)
- {
- TDescriptor specifier;
- TDescriptor selectionSpecifier;
- TDescriptor keyData;
- OSErr err = noErr;
-
- //
- // Build an object specifier for "selection of <null container>"
- //
- selectionSpecifier = MakeSpecifierForSelection();
-
- Try
- {
- //
- // Next, make a specifier for "property of selection"
- //
- specifier = MakeSpecifierForPropertyOfSpecifier(property, selectionSpecifier);
- selectionSpecifier.Dispose();
- }
- Catch(err)
- {
- //
- // If 'MakeSpecifierForPropertyOfSpecifier' fails, we must dispose
- // of selectionSpecifier.
- //
- selectionSpecifier.Dispose();
- Throw(err);
- }
-
- return specifier;
- } // MakeSpecifierForPropertyOfSelection
-
- TDescriptor GetFinderSelection(DescType desiredType);
-
- #ifdef SOMEWHEREELSE
-
- //----------------------------------------------------------------------------------------
- // GetFinderSelection:
- //
- // This routine asks the Finder for the list of selected items; 'desiredType' specifies
- // the data type that is desired in the result that is sent back; choices include
- // typeObjectSpecifier, typeFSS, and typeAlias.
- //----------------------------------------------------------------------------------------
- TDescriptor GetFinderSelection(DescType desiredType)
- {
- TAEvent ae;
- TAEvent reply;
- TDescriptor target;
- TDescriptor directObjectSpecifier;
- TDescriptor dataDescriptor;
- TDescriptor selectedItems;
- OSErr err = noErr;
-
- Try
- {
- //
- // Get the address of the Finder and make a "Set Data" event
- //
- target = GetAddressOfFinder();
- ae.MakeAppleEvent(kAECoreSuite, kAEGetData, target);
- target.Dispose();
-
- //
- // Make an object specifier for the property we want to set,
- // and put it into the direct object of our event
- //
- directObjectSpecifier = MakeSpecifierForSelection();
- ae.PutDescriptor(keyDirectObject, directObjectSpecifier);
- directObjectSpecifier.Dispose();
-
- //
- // Request FSSpecs back from the Get Data event.
- //
- // The Object Support Library requires that the
- // requested data type be stored in a list of types,
- // so we coerce the 'typeType' descriptor into a list
- // of one descriptor. The Finder does not require
- // this, but some other applications might, since it
- // is part of the OSL spec.
- //
- dataDescriptor.MakeDescType(desiredType);
- dataDescriptor.Coerce(typeAEList);
- ae.PutDescriptor(keyAERequestedType, dataDescriptor);
- dataDescriptor.Dispose();
-
- //
- // It is generally a bad idea to use kAEWaitReply,
- // because it prevents your application from processing
- // other events. There are a number of potential solutions:
- //
- // Use kAEQueueReply, and process the reply when it is
- // returned from WaitNextEvent. The disadvantage is that
- // the processing of the reply must be done separately
- // from the code that sets up to do the send
- //
- // Provide a filter proc to AESend that processes
- // events while your application is waiting for the reply.
- // The disadvantage of doing this is that the event
- // handling of your application is complicated, and
- // may become nested if events need to be sent to
- // process an incoming message
- //
- // Pass kImmediateTimeout as the timeout value for
- // AESend. When the reply event is accessed, an
- // error is returned if the reply has not yet arrived.
- // This method is best employeed in conjunction with
- // a threads package, so that the thread that sends
- // the message can block until the reply arrives.
- // See the article on Futures by Michael Gough in
- // d e v e l o p issue #7.
- //
- ae.Send(&reply, kAEWaitReply);
-
- //
- // Extract the result from the reply
- //
- selectedItems = reply.GetDescriptor(keyAEResult);
- reply.Dispose();
- }
- Catch(err)
- {
- target.Dispose();
- directObjectSpecifier.Dispose();
- dataDescriptor.Dispose();
- reply.Dispose();
-
- Throw(err);
- }
-
- return selectedItems;
- } // GetFinderSelection
-
- #endif
-
- //----------------------------------------------------------------------------------------
- // CountItemsInContainer
- //
- // Returns the number of items of class 'desiredClass' inside the specified
- // container. The target application is specified in the parameter list.
- //----------------------------------------------------------------------------------------
- long CountItemsInContainer(DescType desiredClass, TDescriptor inContainer, TDescriptor target)
- {
- TAEvent ae;
- TAEvent reply;
- TDescriptor keyData;
- OSErr err = noErr;
- long theCount = 0;
-
- Try
- {
- //
- // Make a "Count elements" event
- //
- ae.MakeAppleEvent(kAECoreSuite, kAECountElements, target);
-
- //
- // Put the direct object specifier into the direct object of our event
- //
- ae.PutDescriptor(keyDirectObject, inContainer);
-
- //
- // Specify the class of things we want to count
- //
- keyData.MakeDescType(desiredClass);
- ae.PutDescriptor(keyAEObjectClass, keyData);
- keyData.Dispose();
-
- //
- // Ask the question. kAEWaitReply without filter procs is evil.
- //
- ae.Send(&reply, kAEWaitReply);
-
- //
- // Extract the result out of the reply
- //
- theCount = reply.GetLongParameter(keyAEResult);
- }
- Catch(err)
- {
- //
- // Any of a number of routines that we call above could
- // fail; if they do, we need to dispose of any object
- // that we created.
- //
- ae.Dispose();
- reply.Dispose();
- keyData.Dispose();
-
- Throw(err);
- }
-
- return theCount;
- } // CountItemsInContainer
-
- //----------------------------------------------------------------------------------------
- // RotateIconsTrick
- //----------------------------------------------------------------------------------------
- void RotateIconsTrick()
- {
- TDescriptor frontWindowSpecifier;
- TDescriptor itemNOfFrontWindowSpecifier;
- TDescriptor iconOfItemNSpecifier;
- TAEvent ae;
- TAEvent reply;
- TDescriptor target;
- TDescriptor iconFamilyForFirstItem;
- long itemsInFrontWindow;
- OSErr err = noErr;
-
- Try
- {
- //
- // Make the address of the finder, and an object specifier to
- // the frontmost window. We will use these descriptors a number
- // of times.
- //
- target = GetAddressOfFinder();
- frontWindowSpecifier = MakeSpecifierForFrontWindow();
-
- //
- // Ask the Finder how many items are in the front window
- //
- itemsInFrontWindow = CountItemsInContainer(cObject, frontWindowSpecifier, target);
-
- //
- // Get an icon family for the first item in the frontmost window
- // and save it away
- //
- ae.MakeAppleEvent(kAECoreSuite, kAEGetData, target);
-
- //
- // Set the direct object for this event to 'icon of item 1 of frontWindowSpecifier'
- //
- itemNOfFrontWindowSpecifier = MakeSpecifierForIndexedItem(cObject, 1, frontWindowSpecifier);
- iconOfItemNSpecifier = MakeSpecifierForPropertyOfSpecifier(pIconBitmap, itemNOfFrontWindowSpecifier);
- ae.PutDescriptor(keyDirectObject, iconOfItemNSpecifier);
- iconOfItemNSpecifier.Dispose();
- itemNOfFrontWindowSpecifier.Dispose();
-
- //
- // See notes on waitreply, elsewhere
- //
- ae.Send(&reply, kAEWaitReply);
-
- //
- // Snarf the icon family out of the reply
- //
- iconFamilyForFirstItem = reply.GetDescriptor(keyAEResult);
-
- //
- // Next, loop through the other items and set each's icon to
- // that of its neighbor
- //
- for(long i=2; i<=itemsInFrontWindow; ++i)
- {
- //
- // Set the icon of item i-1 to the icon of item i.
- // Note that we could get an item from the Finder with a Get Data event,
- // then send a separate Set Data event; however, it's faster to tell the
- // Finder to set the icon of one item to be the icon of another item.
- // To do this, we will set keyAEData to be an object specifier rather than
- // literal data, and the Finder will figure out what's intended
- //
- ae.MakeAppleEvent(kAECoreSuite, kAESetData, target);
-
- //
- // Set the direct object for this event to 'icon of item (i - 1) of frontWindowSpecifier'
- //
- itemNOfFrontWindowSpecifier = MakeSpecifierForIndexedItem(cObject, i - 1, frontWindowSpecifier);
- iconOfItemNSpecifier = MakeSpecifierForPropertyOfSpecifier(pIconBitmap, itemNOfFrontWindowSpecifier);
- ae.PutDescriptor(keyDirectObject, iconOfItemNSpecifier);
- iconOfItemNSpecifier.Dispose();
- itemNOfFrontWindowSpecifier.Dispose();
-
- //
- // Set the key data for this event to 'icon of item i of frontWindowSpecifier'
- //
- itemNOfFrontWindowSpecifier = MakeSpecifierForIndexedItem(cObject, i, frontWindowSpecifier);
- iconOfItemNSpecifier = MakeSpecifierForPropertyOfSpecifier(pIconBitmap, itemNOfFrontWindowSpecifier);
- ae.PutDescriptor(keyAEData, iconOfItemNSpecifier);
- iconOfItemNSpecifier.Dispose();
- itemNOfFrontWindowSpecifier.Dispose();
-
- //
- // We send this set data 'wait reply' because we don't want to blast
- // a bunch of events at the Finder all at once; we want to make sure
- // that one 'set data' finishes before the next is sent.
- //
- ae.Send(&reply, kAEWaitReply);
- }
-
- //
- // Finally, set the icon of the last item in the front window
- // to the icon we saved above the loop.
- //
- ae.MakeAppleEvent(kAECoreSuite, kAESetData, target);
-
- //
- // Set the direct object for this event to 'icon of item itemsInFrontWindow of frontWindowSpecifier'
- //
- itemNOfFrontWindowSpecifier = MakeSpecifierForIndexedItem(cObject, itemsInFrontWindow, frontWindowSpecifier);
- iconOfItemNSpecifier = MakeSpecifierForPropertyOfSpecifier(pIconBitmap, itemNOfFrontWindowSpecifier);
- ae.PutDescriptor(keyDirectObject, iconOfItemNSpecifier);
- iconOfItemNSpecifier.Dispose();
- itemNOfFrontWindowSpecifier.Dispose();
-
- //
- // This time, the key data is the actual icon family to set the icon to,
- // not an object specifier
- //
- ae.PutDescriptor(keyAEData, iconFamilyForFirstItem);
- iconFamilyForFirstItem.Dispose();
-
- //
- // The reply isn't actually filled in due to the kAENoReply flag
- //
- ae.Send(&reply, kAENoReply);
-
- //
- // Dispose the items we used for several events
- //
- target.Dispose();
- frontWindowSpecifier.Dispose();
- }
- Catch(err)
- {
- //
- // Any of a number of routines that we call above could
- // fail; if they do, we need to dispose of any object
- // that we created.
- //
- target.Dispose();
- frontWindowSpecifier.Dispose();
- iconFamilyForFirstItem.Dispose();
-
- Throw(err);
- }
- } // RotateIconsTrick
-
- //----------------------------------------------------------------------------------------
- // SetDescTypePropertyOfSpecifier:
- //
- // This function sets an object specified by the object specifier provided to
- // a new value, given as a descriptor type.
- //----------------------------------------------------------------------------------------
- void SetDescTypePropertyOfSpecifier(TDescriptor directObjectSpecifier, DescType newValue)
- {
- TAEvent ae;
- TAEvent reply;
- TDescriptor target;
- TDescriptor dataDescriptor;
- OSErr err = noErr;
-
- Try
- {
- //
- // Get the address of the Finder and make a "Set Data" event
- //
- target = GetAddressOfFinder();
- ae.MakeAppleEvent(kAECoreSuite, kAESetData, target);
- target.Dispose();
-
- //
- // Put the direct object specifier into the direct object of our event
- //
- ae.PutDescriptor(keyDirectObject, directObjectSpecifier);
-
- //
- // Put the new value into the key data of the "Set Data" event
- //
- dataDescriptor.MakeDescType(newValue);
- ae.PutDescriptor(keyAEData, dataDescriptor);
- dataDescriptor.Dispose();
-
- //
- // The reply isn't actuall filled in due to the kAENoReply flag
- //
- ae.Send(&reply, kAENoReply);
- }
- Catch(err)
- {
- //
- // Any of a number of routines that we call above could
- // fail; if they do, we need to dispose of any object
- // that we created.
- //
- target.Dispose();
- dataDescriptor.Dispose();
- ae.Dispose();
-
- Throw(err);
- }
- } // SetDescTypePropertyOfSpecifier
-
- //----------------------------------------------------------------------------------------
- // SetDescTypePropertyOfFinderSelection:
- //
- // This function sets a property of the Finder's selection. We set the property
- // by sending the Finder a single set data event; we do not care what the return
- // value of the "Set Data" event is.
- //----------------------------------------------------------------------------------------
- void SetDescTypePropertyOfFinderSelection(DescType property, DescType newValue)
- {
- TDescriptor directObjectSpecifier;
- OSErr err = noErr;
-
- //
- // Make an object specifier for the property we want to set,
- // and call SetDescTypePropertyOfSpecifier (above) to send
- // the Finder a 'Set Data' event.
- //
- directObjectSpecifier = MakeSpecifierForPropertyOfSelection(property);
-
- Try
- {
- //
- // The actual AESend is done by 'SetDescTypePropertyOfSpecifier', above.
- //
- SetDescTypePropertyOfSpecifier(directObjectSpecifier, newValue);
- directObjectSpecifier.Dispose();
- }
- Catch(err)
- {
- directObjectSpecifier.Dispose();
- Throw(err);
- }
- } // SetDescTypePropertyOfFinderSelection
-
- //----------------------------------------------------------------------------------------
- // UpdateChangedObject:
- //----------------------------------------------------------------------------------------
- void UpdateChangedObject(FSSpec& itemsFSSpec)
- {
- TAEvent ae;
- TAEvent reply;
- TDescriptor target;
- TDescriptor directObjectSpecifier;
- OSErr err = noErr;
-
- Try
- {
- //
- // Get the address of the Finder and make an "Update" event
- //
- target = GetAddressOfFinder();
- ae.MakeAppleEvent(kAEFinderSuite, kAEUpdate, target);
- target.Dispose();
-
- //
- // Most scriptable applications require the direct
- // object of an event to always be an object
- // specifier. The Finder is special, though; it
- // will accept FSSpecs and alias records as well.
- //
- directObjectSpecifier.MakeFSS(itemsFSSpec);
- ae.PutDescriptor(keyDirectObject, directObjectSpecifier);
- directObjectSpecifier.Dispose();
-
- //
- // The reply isn't actuall filled in due to the kAENoReply flag
- //
- ae.Send(&reply, kAENoReply);
- }
- Catch(err)
- {
- //
- // Any of a number of routines that we call above could
- // fail; if they do, we need to dispose of any object
- // that we created.
- //
- target.Dispose();
- directObjectSpecifier.Dispose();
- ae.Dispose();
-
- Throw(err);
- }
- } // UpdateChangedObject
-
- //
- // Listing 3 returns owner of front window
- //
- TDescriptor Listing3();
-
- void RevealOwnerOfFrontWindow()
- {
- TAEvent ae;
-
- TDescriptor target = GetAddressOfFinder();
- ae.MakeAppleEvent(kAEMiscStandards, kAEMakeObjectsVisible, target);
- target.Dispose();
-
- TDescriptor ownerOfFrontWindow = Listing3();
-
- ae.PutDescriptor(keyDirectObject, ownerOfFrontWindow);
- ownerOfFrontWindow.Dispose();
-
- TAEvent reply;
- ae.Send(&reply, kAENoReply);
- }
-
- //
- // Listing 4 deletes the custom icon of every item in the selection
- //
- void Listing4();
-
- void RemoveCustomIconFromSelection()
- {
- Listing4();
- }
-
- //
- // Listing 6 shares every folder in the selection
- //
- void Listing6();
-
- void ShareSelectedFolders()
- {
- Listing6();
- }
-
-